package services

import (
	"context"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/time"
	"go.uber.org/zap"
)

type AccessRecordService interface {
	Create(ctx context.Context, record *models.AccessRecord) error
	FindByUID(ctx context.Context, UID string, graphType models.AccessGraphType, keyword string, page int, size int) ([]*models.AccessRecord, int64, error)
}

type accessRecordService struct {
	logger                 *zap.Logger
	accessRecordRepository repositories.AccessRecordRepository
	appRepo                repositories.ApplicationRepository
	apiRepo                repositories.ApiRepository
	dashboardRepo          repositories.DashboardRepository
	hostRepo               repositories.HostRepository
	envID                  uint64
}

func NewAccessRecordService(logger *zap.Logger, v *viper.Viper,
	accessRecordRepository repositories.AccessRecordRepository, appRepo repositories.ApplicationRepository,
	apiRepo repositories.ApiRepository, hostRepo repositories.HostRepository,
	dashboardRepo repositories.DashboardRepository) AccessRecordService {
	return &accessRecordService{
		logger:                 logger,
		accessRecordRepository: accessRecordRepository,
		appRepo:                appRepo,
		apiRepo:                apiRepo,
		dashboardRepo:          dashboardRepo,
		hostRepo:               hostRepo,
		envID:                  v.GetUint64("app.env"),
	}
}

func (a *accessRecordService) FindByUID(ctx context.Context, UID string, graphType models.AccessGraphType, keyword string, page int, size int) ([]*models.AccessRecord, int64, error) {
	var accessRecords []*models.AccessRecord
	var total int64
	var err error
	switch graphType {
	case models.DashboardAccess:
		dashboard, count, err := a.dashboardRepo.FindByName(ctx, keyword, page, size)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call dashboardRepo.FindAll err")
		}
		for _, d := range dashboard {
			accessRecords = append(accessRecords, &models.AccessRecord{
				DashboardID: d.ID,
				Name:        d.Name,
				GraphType:   models.DashboardAccess,
			})
		}
		total = count
	case models.AppAccess:
		apps, count, err := a.appRepo.FindByNameAndGroupForPage(ctx, keyword, "", 0, size, page)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepo.FindByNameAndGroupForPage err")
		}
		for _, app := range apps {
			accessRecords = append(accessRecords, &models.AccessRecord{
				AppID:     app.ID,
				Name:      app.Name,
				GraphType: models.AppAccess,
			})
		}
		total = count
	case models.ApiAccess:
		apiInfos, count, err := a.apiRepo.FindByQuery(ctx, keyword, page, size)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepo.FindByNameAndGroupForPage err")
		}
		for _, info := range apiInfos {
			accessRecords = append(accessRecords, &models.AccessRecord{
				ApiID:     info.ID,
				Name:      info.GetMethod(),
				GraphType: models.ApiAccess,
			})
		}
		total = count
	case models.HostAccess:
		hosts, count, err := a.hostRepo.FindByEnvIDAndGroupAndDCAndNameAndLikeName(ctx, a.envID, "", "", "", keyword, uint64(page), uint64(size))
		if err != nil {
			return nil, 0, errors.Wrap(err, "call hostRepo.FindByEnvIDAndGroupAndDCAndNameAndLikeName err")
		}
		for _, host := range hosts {
			accessRecords = append(accessRecords, &models.AccessRecord{
				Name:      host.HostName,
				IP:        host.IPOutside,
				GraphType: models.HostAccess,
			})
		}
		total = count
	default:
		accessRecords, total, err = a.accessRecordRepository.FindByUID(ctx, UID, keyword, graphType, page, size)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call accessRecordRepository.FindByUID err")
		}
	}

	return accessRecords, total, nil
}

func (a *accessRecordService) Create(ctx context.Context, record *models.AccessRecord) error {
	record.CreateTime = time.Now()
	return a.accessRecordRepository.Create(ctx, record)
}
